package org.pupil.tolerant.impl;

import io.netty.channel.ChannelFuture;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;
import org.pupil.common.*;
import org.pupil.socket.codec.RpcResponse;
import org.pupil.tolerant.FaultContext;
import org.pupil.tolerant.FaultTolerantStrategy;

import java.util.Iterator;
import java.util.List;


/**
 * @Description: 容错策略：故障转移
 * @Author: pupil
 * @Date: 2024/07/17 下午 3:56
 */
public class FailoverFaultTolerantStrategy implements FaultTolerantStrategy {

    @Override
    public Object handler(FaultContext faultContext) throws Exception {
        // 获取当前服务提供方资源定位符
        final URL currentURL = faultContext.getCurrentURL();
        // 获取当前服务提供方资源定位符集合
        final List<URL> urls = faultContext.getUrls();
        // 获取集合的迭代器
        final Iterator<URL> iterator = urls.iterator();
        // 进行迭代从集合中删除当前服务提供方资源定位符
        while (iterator.hasNext()) {
            if (iterator.next().equals(currentURL)) {
                // 删除当前服务提供方资源定位符
                iterator.remove();
            }
        }
        // 判断有无服务可用
        if (urls.isEmpty()) {
            throw new Exception("服务提供方发生异常,触发故障容错机制: 故障转移,无服务可用");
        }
        // 获取集合中的第一个服务提供方资源定位符
        final URL url = urls.get(0);
        // 根据服务提供方资源定位符获取对应channelFuture
        final ChannelFuture channelFuture = Cache.CHANNEL_FUTURE_MAP.get(new Host(url.getIP(), url.getPort()));
        // 重新发送消息
        channelFuture.channel().writeAndFlush(faultContext.getRpcProtocol());
        RpcFuture<RpcResponse> future = new RpcFuture<>(new DefaultPromise<>(new DefaultEventLoop()), faultContext.getTime());
        // 根据请求id更新异步监听器
        RpcRequestHolder.REQUEST_MAP.put(faultContext.getRequestId(),future);
        RpcResponse rpcResponse = future.getPromise().sync().get(future.getTimeOut(), faultContext.getTimeUnit());
        // 判断响应数据是否为错误信息
        if (rpcResponse.getException() != null){
            // 设置新获取的服务提供方资源定位符为当前服务提供方资源定位符
            faultContext.setCurrentURL(url);
            // 再次调用容错机制方法
            return handler(faultContext);
        }
        return rpcResponse.getData();
    }
}
